home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’95
/
Venus
/
image.h
< prev
next >
Wrap
Text File
|
1995-06-23
|
17KB
|
533 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Grayscale Image
*
* The image is represented as a Pixmap, i.e. a matrix of pixels
* each of them specifies the gray level at a particular point
*
* The header file defines arithmetical and all other operations
* permitted on the grayscale image
*
* $Id: image.h,v 1.11 1993/05/28 14:34:42 oleg Exp oleg $
*
************************************************************************
*/
#pragma once
#ifndef _image_h
#define _image_h
#ifdef __GNUC__
#pragma interface
#endif
#include "myenv.h"
typedef unsigned short GRAY; // Pixel type
typedef signed short GRAY_SIGNED; // Pixel type, signed
const int GRAY_MAXBIT = 8*sizeof(GRAY); // Max no of bits per pixel
const int GRAY_MAXVAL = ((1<<GRAY_MAXBIT)-1);
class Rectangle;
class rowcol;
class Extrema;
typedef int USER_F(int); // User function that can be applied
// to the image
class IMAGE
{
friend class Rectangle;
friend class Extrema;
private: // Private part
int valid_code; // Validation code
#define IMAGE_val_code 577767 // Image validation code
int ncols; // Image width in pixels
int nrows; // Image height in pixels
int npixels; // Total no of pixels in the image
char * name; // Image name
int bits_per_pixel; // Image depth
GRAY ** scanrows; // scanrows[i] = &pixels[i,0]
GRAY * pixels; // pixels[i,j] is the
// pixel value at point (i,j)
// Pixels are ordered in rows
void allocate(const int nrows, const int ncols, const int depth);
void _expand(const IMAGE& prototype); // Expand the prototype twice
void _shrink(const IMAGE& prototype); // Shrink the prototype twice
public: // Public interface
// Constructors and destructors
// Make a blank image
IMAGE(const int nrows, const int ncols, const int depth);
IMAGE(const IMAGE &image); // Make a new blank image like
// the old one
// Read an image from the file
IMAGE(const char * file_name,const char print_header_info = 0);
IMAGE(const Rectangle& area); // Make an image from a square area
// of another image
// Construct an image applying a spec
// operation to the prototype
enum IMAGE_CREATORS_1op { Expand, Shrink };
IMAGE(const IMAGE_CREATORS_1op op, const IMAGE& prototype);
~IMAGE();
void is_valid() const
{ assure(valid_code == IMAGE_val_code,"Invalid image"); }
// Status
int q_nrows() const { return nrows; }
int q_ncols() const { return ncols; }
int q_depth() const { return bits_per_pixel; }
int q_npixels() const { return npixels; }
const char * q_name() const { return name; }
// Individual pixel manipulations
inline GRAY& operator () (const int row, const int col) const;
inline GRAY& operator () (const rowcol pos) const;
void sure_within(const rowcol pos) const; // Make sure the pos
// is within the image
// Row Column, Square operations
// Image-scalar operations
// Find out if the predicate
// "(signed)pixel op val" is true for ALL
// pixels of the image?
int operator == (const int val) const; // ? (signed)pixels == val
int operator != (const int val) const; // ? (signed)pixels != val
int operator < (const int val) const; // ? (signed)pixels < val
int operator <= (const int val) const; // ? (signed)pixels <= val
int operator > (const int val) const; // ? (signed)pixels > val
int operator >= (const int val) const; // ? (signed)pixels >= val
// Modify every element of the
// image according to the operation
IMAGE& operator = (const int val); // Assignment to all the pixels
IMAGE& operator -= (const int val); // Diminish the brightness
IMAGE& operator += (const int val); // Increase the brightness
IMAGE& operator *= (const int val);
IMAGE& operator |= (const int val); // OR
IMAGE& operator &= (const int val); // AND
IMAGE& operator ^= (const int val); // XOR
IMAGE& operator <<= (const int val); // Shift all the pixels
IMAGE& operator >>= (const int val); // Shift all the pixels
// Single image operations
IMAGE& clear(void); // Clear the image
IMAGE& invert(void); // Invert the image
IMAGE& abs(void); // pixel = |(signed)pixel|
IMAGE& clip_to_intensity_range(void); // Clip pixel values to
// [0,1<<bits_per_pixel-1]
IMAGE& normalize_for_display(void); // Normalize pixel values to be
// in range 0..1<<bits_per_pixel-1
IMAGE& equalize(const int no_grays); // Perform the histogram equalization
IMAGE& apply(USER_F * fp); // Apply any function defined for
// a pixel to all pixels in the image
// Estimate the norm of the (signed) image
double norm_1(void) const; // SUM{ |(signed)pixel[i,j]| }
double norm_2_sqr(void) const; // SUM{ (signed)pixel[i,j]^2 }
int norm_inf(void) const; // MAX{ |(signed)pixel[i,j]| }
// Two images operations
IMAGE& operator = (const IMAGE& source); // Assignment
friend int identical(const IMAGE& im1, const IMAGE& im2);
friend inline int operator == (const IMAGE& im1, const IMAGE& im2); // Alias
friend void compare(const IMAGE& im1, const IMAGE& im2,
const char * title);
friend inline void are_compatible(const IMAGE& im1, const IMAGE& im2);
// Arithmetics
friend IMAGE& operator += (IMAGE& target, const IMAGE& source);
friend IMAGE& operator -= (IMAGE& target, const IMAGE& source);
friend IMAGE& add(IMAGE& target, const int scalar,const IMAGE& source);
// Shift the source to 'pos'
// clip if necessary
// multiply by scalar
// and add
IMAGE& shift_clip_add(rowcol pos, const int scalar, const IMAGE& source);
// Logic
friend IMAGE& operator |= (IMAGE& target, const IMAGE& source);
friend IMAGE& operator &= (IMAGE& target, const IMAGE& source);
friend IMAGE& operator ^= (IMAGE& target, const IMAGE& source);
// Scalar product
friend double operator * (const IMAGE& im1, const IMAGE& im2);
// Estimate the norm of the difference
// between two (signed) image
// SUM{ |(signed)pixel[i,j]| }
friend double norm_1(const IMAGE& im1, const IMAGE& im2);
// SUM{ (signed)pixel[i,j]^2 }
friend double norm_2_sqr(const IMAGE& im1, const IMAGE& im2);
// MAX{ |(signed)pixel[i,j]| }
friend int norm_inf(const IMAGE& im1, const IMAGE& im2);
// I/O: write, read, display, print info
// Write to a file
// "| command name" is OK as a file
// name
void write_pgm(const char * file_name,const char * title = "") const;
// Default writer
void write(const char * file_name,const char * title = "") const
{ write_pgm(file_name,title); }
void display(const char * title) const;
void info(void) const; // Print the info about the image
void print(const char * title) const; // Print the image as a table
// Clip a square area of the image
Rectangle square_of (const int size, const rowcol pos) const;
// Clip a rectangular area of the image
// specified by the coordinates of
// the upper-left and lower-right corners
Rectangle rectangle (const rowcol uppleft, const rowcol lowright) const;
// misc
volatile void error(const char* msg) const;
};
/*
*------------------------------------------------------------------------
* Position specification and operations on it
*/
class rowcol { // Specifying the row/col position
friend class IMAGE;
friend class Extrema;
protected:
int row_val; // both row, col start from 0
int col_val;
public:
// Constructors
rowcol(const int row, const int col) : row_val(row), col_val(col) {}
rowcol(void) : row_val(-1), col_val(-1) {}
~rowcol() {}
int row(void) const { return row_val; }
int col(void) const { return col_val; }
// Assignments
// Note, that the implementation takes
// advantage and is dependent of the
// fact the entire rowcol structure
// fits into one long word
rowcol& operator = (const rowcol& pos)
{ *((long int *)this) = *((long int *)&pos); return *this; }
rowcol(const rowcol& pos)
{ *((long int *)this) = *((long int *)&pos); }
int operator == (const rowcol& pos)
{ return *((long int *)this) == *((long int *)&pos); }
// { return memcmp(this,&pos,sizeof(rowcol)); }
// Offset the current position
rowcol& operator += (const rowcol& pos)
{ row_val += pos.row_val; col_val += pos.col_val; return *this; }
rowcol& operator -= (const rowcol& pos)
{ row_val -= pos.row_val; col_val -= pos.col_val; return *this; }
friend inline rowcol operator + (const rowcol& pos1, const rowcol& pos2);
friend inline rowcol operator - (const rowcol& pos1, const rowcol& pos2);
// Scale the current position
rowcol& operator *= (const int scalef)
{ row_val *= scalef; col_val *= scalef; return *this; }
friend inline rowcol operator * (const rowcol& pos, const int scalef);
friend inline rowcol operator << (const rowcol& pos, const int shiftf);
friend inline rowcol operator >> (const rowcol& pos, const int shiftf);
};
inline rowcol operator + (const rowcol& pos1, const rowcol& pos2)
{ rowcol rc(pos1.row_val+pos2.row_val, pos1.col_val+pos2.col_val); return rc;}
inline rowcol operator - (const rowcol& pos1, const rowcol& pos2)
{ rowcol rc(pos1.row_val-pos2.row_val, pos1.col_val-pos2.col_val); return rc;}
inline rowcol operator * (const rowcol& pos, const int scalef)
{ rowcol rc(pos.row_val*scalef, pos.col_val*scalef); return rc; }
inline rowcol operator >> (const rowcol& pos, const int shiftf)
{ rowcol rc(pos.row_val >> shiftf, pos.col_val >> shiftf); return rc;}
inline rowcol operator << (const rowcol& pos, const int shiftf)
{ rowcol rc(pos.row_val << shiftf, pos.col_val << shiftf); return rc;}
class Extrema // Find min/max values of the (signed) image
{
GRAY_SIGNED max_value; // Max pixel value in the image
GRAY_SIGNED min_value; // Min pixel value in the image
public:
rowcol max_pixel; // Position of the largest pixel
rowcol min_pixel; // Position of the smallest pixel
Extrema(const IMAGE& image); // Find extremum pixels of the image
~Extrema() {}
GRAY_SIGNED max(void) const { return max_value; }
GRAY_SIGNED min(void) const { return min_value; }
};
/*
*------------------------------------------------------------------------
* Dealing with the rectangular area of the image
*/
class Rectangle
{
friend class IMAGE;
IMAGE& image; // The image I'm a rectangle of
int nrows; // Dimension of the rectangle
int ncols;
GRAY * ptr; // Pointer to the upper left corner of
// the rectangle
int inc_to_nextrow; // inc_to_nextrow = p' - p =
// = image.ncols - ncols
// where
// p = ptr + ncols points to the pixel
// just beyond the current scanline of
// the Rectangle
// p' = (ptr - col) + image.ncols + col
// points to the first pixel of the next
// scanline of the rectangtle
// col tells the column of the upper left
// corner of the rectangle within the image
// Private constructor
// Note these are private constructors to
// be used with IMAGE::square_of/rectangle
// functions
Rectangle
(IMAGE& im, const int size, const rowcol pos);
Rectangle
(IMAGE& im, const rowcol uppleft, const rowcol lowright);
public:
// Note how to construct square area of
// the image 'im':
// im.square_of(10,rowcol(1,5))
// and rectangular area
// im.rectangle(rowcol(0,1),rowcol(4,5))
Rectangle (IMAGE& im); // Treat entire image as a rectangle
~Rectangle(void) {}
// Assign a value to all the pixels
// in the rectangle area
Rectangle& operator = (const int val);
// Modify the pixels in the rectangle
// area
friend void operator += (const Rectangle& sa, const int val);
friend void operator -= (const Rectangle& sa, const int val);
friend void operator *= (const Rectangle& sa, const int val);
friend void operator |= (const Rectangle& sa, const int val);
friend void operator &= (const Rectangle& sa, const int val);
friend void operator ^= (const Rectangle& sa, const int val);
friend void operator <<= (const Rectangle& sa, const int val);
friend void operator >>= (const Rectangle& sa, const int val);
// Copy a rectangle a_rect into the
// rectangular area of the image
Rectangle& operator = (const Rectangle& a_rect);
// Get a total sum of all the pixels
friend double sum_over(const Rectangle& sa);
};
#if 0
/*
*------------------------------------------------------------------------
* Some service procedures
*/
inline
int log2(const int n ) // Find a binary logarithm of n
{ // and check that n is a power of two
register int i,k;
assure(n > 0, "log2: the argument's got to be positive!");
for(k=0,i=1; i < n; k++, i*=2)
;
if( i != n )
_error("log2: the argument %d has got to be an exact power of two",n);
return k;
}
inline
int exp2(const int k) // Compute 2^k, k>=0
{
assure(k >= 0, "exp2: the argument may not be negative!");
if( k > (signed)sizeof(int)*8-1 )
_error("exp2: the exponent %d is too big",k);
return 1<<k;
}
inline // return b * round( a/b )
int round_to_even_multiple(const int a, const int b)
{
assert( b > 0 );
register int rem = a % b;
if( 2*rem < b )
return a - rem;
else
return a + b - rem;
}
#endif
/*
*------------------------------------------------------------------------
* Inline Image Procedures
*/
inline IMAGE::IMAGE(const int no_rows, const int no_cols, const int depth)
{
allocate(no_rows,no_cols,depth);
}
// Make a new image like the
inline IMAGE::IMAGE(const IMAGE& old) // old one
{
allocate(old.nrows,old.ncols,old.bits_per_pixel);
}
inline GRAY& IMAGE::operator () (const int row, const int col) const
{
is_valid();
if( row >= nrows || row < 0 )
_error("Row index %d is out of image boundaries [0,%d]",row,nrows-1);
if( col >= ncols || col < 0 )
_error("Col index %d is out of image boundaries [0,%d]",col,ncols-1);
return (scanrows[row])[col];
}
inline GRAY& IMAGE::operator () (const rowcol pos) const
{
return operator()(pos.row(),pos.col());
}
// Make sure the pos is within the
// image
inline void IMAGE::sure_within(const rowcol pos) const
{
is_valid();
if( pos.row() >= nrows || pos.row() < 0 )
_error("Row index %d is out of image boundaries [0,%d]",pos.row(),nrows-1);
if( pos.col() >= ncols || pos.col() < 0 )
_error("Col index %d is out of image boundaries [0,%d]",pos.col(),ncols-1);
}
inline IMAGE& IMAGE::clear(void) // Clean the image
{
is_valid();
memset(pixels,0,npixels*sizeof(GRAY));
return *this;
}
inline IMAGE& IMAGE::invert(void) // Invert the image
{
is_valid();
return (*this ^= ((1<<bits_per_pixel)-1));
}
inline void are_compatible(const IMAGE& im1, const IMAGE& im2)
{
im1.is_valid();
im2.is_valid();
if( im1.ncols != im2.ncols || im1.nrows != im2.nrows )
_error("The image %dx%d and the image %dx%d have different sizes",
im1.nrows,im1.ncols,im2.nrows,im2.ncols);
}
inline int operator == (const IMAGE& im1, const IMAGE& im2)
{
return identical(im1,im2);
}
// Construct a square rectangle
inline Rectangle::Rectangle
(IMAGE& im, const int sq_size, const rowcol pos)
: image(im), nrows(sq_size), ncols(sq_size)
{
image.is_valid();
if( pos.row() >= image.nrows || pos.row() < 0 ||
pos.row() + nrows > image.nrows ||
pos.col() >= image.ncols || pos.col() < 0 ||
pos.col() + ncols > image.ncols )
_error("Square area (left upper point [%d,%d], size %d)\n"
"is not within the image %dx%d",
pos.row(),pos.col(),sq_size,image.nrows,image.ncols);
ptr = &(image.scanrows[pos.row()][pos.col()]);
inc_to_nextrow = image.ncols - ncols;
}
// Clip a square area from the image
inline Rectangle IMAGE::square_of(const int size, const rowcol pos) const
{ Rectangle c((IMAGE)*this, size, pos); return c; }
// Make a rectangle with upper left corner
// at uppleft position and lower right corner
// at lowright position
inline Rectangle::Rectangle
(IMAGE& im, const rowcol uppleft, const rowcol lowright)
: image(im), nrows(lowright.row()-uppleft.row()+1),
ncols(lowright.col()-uppleft.col()+1)
{
image.is_valid();
image.sure_within(uppleft);
image.sure_within(lowright);
ptr = &(image.scanrows[uppleft.row()][uppleft.col()]);
inc_to_nextrow = image.ncols - ncols;
}
// Clip a rectangular area from the image
inline Rectangle IMAGE::rectangle(const rowcol uppleft,const rowcol lowright)
const
{ Rectangle c((IMAGE)*this, uppleft, lowright); return c; }
// Treat the entire image as a rectangular area
inline Rectangle::Rectangle (IMAGE& im)
: image(im), nrows(im.nrows), ncols(im.ncols)
{
image.is_valid();
ptr = image.pixels;
inc_to_nextrow = 0;
}
#endif